using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;

namespace TelnetTests
{
    class OneByteAtATimeStream : Stream
    {
        Stream m_child;

        public OneByteAtATimeStream(Stream child)
        {
            m_child = child;
        }

        public override bool CanRead
        {
            get { return m_child.CanRead; }
        }

        public override bool CanSeek
        {
            get { return m_child.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return m_child.CanWrite; }
        }

        public override void Flush()
        {
            m_child.Flush();
        }

        public override long Length
        {
            get { return m_child.Length; }
        }

        public override long Position
        {
            get { return m_child.Position; }
            set { m_child.Position = value; }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return m_child.Read(buffer, offset, Math.Min(1, count));
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override void SetLength(long value)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }


    class PairNoCanSeekStream : PairStream
    {
        public PairNoCanSeekStream(Stream read, Stream write)
            : base(read, write)
        {
        }

        //----
        public override bool CanSeek { get { return false; } }
    }


    class PairStream : Stream
    {
        Stream m_write, m_read;

        public PairStream(Stream read, Stream write)
        {
            m_write = write;
            m_read = read;
        }

        //----
        public override int Read(byte[] buffer, int offset, int count)
        {
            return m_read.Read(buffer, offset, count);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            m_write.Write(buffer, offset, count);
        }

        //----
        public override bool CanRead
        {
            get { return m_read.CanRead; }
        }

        public override bool CanSeek
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public override bool CanWrite
        {
            get { return m_write.CanWrite; }
        }

        //----
        public override void Flush()
        {
            m_write.Flush();
        }

        public override long Length
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public override long Position
        {
            get { throw new Exception("The method or operation is not implemented."); }
            set { throw new Exception("The method or operation is not implemented."); }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override void SetLength(long value)
        {
            throw new Exception("The method or operation is not implemented.");
        }

    }


    //TODO rename class FooStream
    class FooStream : Stream
    {
        volatile bool m_closed;
        Queue<byte> m_readBuf = new Queue<byte>();
        object m_lock = new object();
        ManualResetEvent m_emptied = new ManualResetEvent(false);
        //
        ManualResetEvent m_didWrite = new ManualResetEvent(false);
        Stream m_toWriteTo;

        //----
        public FooStream()
        {
        }

        public FooStream(Stream toWriteTo)
        {
            m_toWriteTo = toWriteTo;
        }

        //----
        protected override void Dispose(bool disposing)
        {
            bool entered = false;
            try {
                m_closed = true;
                entered = Monitor.TryEnter(m_lock);
            } finally {
                if (entered) {
                    Monitor.Pulse(m_lock);
                    Monitor.Exit(m_lock);
                }
                //
                //if (disposing && m_toWriteTo != null) {
                //    m_toWriteTo.Close();
                //}
                //
                base.Dispose(disposing);
            }
        }
        //----
        internal void SendToReadAndWaitDidWrite(byte[] buf)
        {
            m_didWrite.Reset();
            SendToRead(buf);
            SafeWait(m_didWrite);
        }

        public void SendToReadAndWaitEmptied(byte[] buf)
        {
            m_emptied.Reset();
            SendToRead(buf);
            SafeWait(m_emptied);
        }

        private void SafeWait(ManualResetEvent e)
        {
            bool signalled = e.WaitOne(new TimeSpan(0, 0, 30));
            Debug.Assert(signalled);
        }

        public void SendToRead(byte[] buf)
        {
            lock (m_lock) {
                foreach(byte b in buf){
                    m_readBuf.Enqueue(b);
                }//for
                Monitor.Pulse(m_lock);
            }
        }

        //----
        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override void Flush()
        {
            if (m_toWriteTo != null) {
                m_toWriteTo.Flush();
            }
        }

        public override long Length
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public override long Position
        {
            get
            {
                throw new Exception("The method or operation is not implemented.");
            }
            set
            {
                throw new Exception("The method or operation is not implemented.");
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int i;
            lock (m_lock) {
                while (m_readBuf.Count == 0) {
                    if (m_closed) {
                        return 0;
                    }
                    Monitor.Wait(m_lock);
                }
                for (i = 0; i < count; ++i) {
                    buffer[i + offset] = m_readBuf.Dequeue();
                    if (m_readBuf.Count == 0) {
                        m_emptied.Set();
                        break;
                    }
                }//for
            }
            Debug.Assert(i > 0);
            return i;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override void SetLength(long value)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (m_toWriteTo != null) {
                m_toWriteTo.Write(buffer, offset, count);
            }
            m_didWrite.Set();
        }

    }


    class CanReadFalseStream : Stream
    {
        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanSeek
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public override bool CanWrite
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public override void Flush()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override long Length
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        public override long Position
        {
            get
            {
                throw new Exception("The method or operation is not implemented.");
            }
            set
            {
                throw new Exception("The method or operation is not implemented.");
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override void SetLength(long value)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}
